﻿using System;
using System.Collections.Generic;
using System.Configuration;
using Cesave.Application.Permisos.Settings;
using Cesave.Domain.PermisosModule.Contracts.Acls;
using Cesave.Domain.PermisosModule.Contracts.Acreditaciones;
using Cesave.Domain.PermisosModule.Contracts.Afianzadoras;
using Cesave.Domain.PermisosModule.Contracts.Agentes;
using Cesave.Domain.PermisosModule.Contracts.Areas;
using Cesave.Domain.PermisosModule.Contracts.AreaSecciones;
using Cesave.Domain.PermisosModule.Contracts.Ciclos;
using Cesave.Domain.PermisosModule.Contracts.CiclosFechas;
using Cesave.Domain.PermisosModule.Contracts.Comercializadoras;
using Cesave.Domain.PermisosModule.Contracts.Concesiones;
using Cesave.Domain.PermisosModule.Contracts.CultivosEstaciones;
using Cesave.Domain.PermisosModule.Contracts.CultivosNumeros;
using Cesave.Domain.PermisosModule.Contracts.Distritos;
using Cesave.Domain.PermisosModule.Contracts.DistritosPersonal;
using Cesave.Domain.PermisosModule.Contracts.Ejidos;
using Cesave.Domain.PermisosModule.Contracts.Estados;
using Cesave.Domain.PermisosModule.Contracts.FoliosPermisos;
using Cesave.Domain.PermisosModule.Contracts.Juntas;
using Cesave.Domain.PermisosModule.Contracts.Modulos;
using Cesave.Domain.PermisosModule.Contracts.Municipios;
using Cesave.Domain.PermisosModule.Contracts.RiegoFuentes;
using Cesave.Domain.PermisosModule.Contracts.RiegoSistemas;
using Cesave.Domain.PermisosModule.Contracts.Secciones;
using Cesave.Domain.PermisosModule.Contracts.Tenencias;
using Cesave.Domain.PermisosModule.Contracts.Usuarios;
using Cesave.Infrastructure.CrossCutting.IoC.LifetimeManagers;
using Cesave.Infrastructure.CrossCutting.IoC.Resources;
using Cesave.Infrastructure.CrossCutting.Logging;
using Cesave.Infrastructure.CrossCutting.NetFramework.Logging;
using Cesave.Infrastructure.CrossCutting.NetFramework.Network;
using Cesave.Infrastructure.CrossCutting.Network;
using Cesave.Infrastructure.Data.PermisosModule.Context;
using Cesave.Infrastructure.Data.PermisosModule.Mock;
using Cesave.Infrastructure.Data.PermisosModule.Repositories;
using Microsoft.Practices.Unity;
using Cesave.Domain.PermisosModule.Contracts.CultivosClasificaciones;
using Cesave.Domain.PermisosModule.Contracts.Cultivos;
using Cesave.Domain.PermisosModule.Contracts.CultivosAguas;
using Cesave.Domain.PermisosModule.Contracts.CultivosPropiedades;
using Cesave.Domain.PermisosModule.Contracts.CultivosFechas;
using Cesave.Domain.PermisosModule.Contracts.VariedadesGrupos;
using Cesave.Domain.PermisosModule.Contracts.Variedades;
using Cesave.Domain.PermisosModule.Contracts.ProduccionTipos;
using Cesave.Domain.PermisosModule.Contracts.Financiamientos;

namespace Cesave.Infrastructure.CrossCutting.IoC
{
    /// <summary>
    /// Inversion of Control factory implementation.
    /// This is a simple factory built with Microsoft Unity    
    /// </summary>
    public static class IoCFactory
    {
        #region Members

        private static IDictionary<string, IUnityContainer> _containersDictionary;

        #endregion

        #region Constructor

        /// <summary>
        /// Wires up the ioc containers
        /// <remarks>
        /// This static constructor remove 'beforeFieldInit' IL annotation
        /// </remarks>
        /// </summary>        
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
        static IoCFactory()
        {
            InitContainers();
        }

        #endregion

        #region Private Methods

        static void InitContainers()
        {
            /*
             * CREATE DICTIONARY
             */
            _containersDictionary = new Dictionary<string, IUnityContainer>();

            /*
             * CREATE CONTAINERS 
             */
            //Create root container
            IUnityContainer rootContainer = new UnityContainer();
            _containersDictionary.Add("RootContext", rootContainer);

            //Create container for real context, child of root container
            IUnityContainer realAppContainer = rootContainer.CreateChildContainer();
            _containersDictionary.Add("RealAppContext", realAppContainer);

            //Create container for testing, child of root container
            IUnityContainer fakeAppContainer = rootContainer.CreateChildContainer();
            _containersDictionary.Add("FakeAppContext", fakeAppContainer);

            /*
             * CONFIGURE CONTAINERS
             */
            ConfigureRootContainer(rootContainer);
            ConfigureRealContainer(realAppContainer);
            ConfigureFakeContainer(fakeAppContainer);
        }

        /// <summary>
        /// Configure root container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        static void ConfigureRootContainer(IUnityContainer container)
        {
            // Take into account that Types and Mappings registration could be also done using the UNITY XML configuration
            //But we prefer doing it here (C# code) because we'll catch errors at compiling time instead execution time, if any type has been written wrong.

            //Register application settings
            container.RegisterType<IPUSSettings, PUSSettings>(new ContainerControlledLifetimeManager());

            //Register Repositories mappings
			container.RegisterType<IEstadoRepository, EstadoRepository>(new TransientLifetimeManager());
			container.RegisterType<IDistritoRepository, DistritoRepository>(new TransientLifetimeManager());
			container.RegisterType<ICicloRepository, CicloRepository>(new TransientLifetimeManager());
			container.RegisterType<IDistritoPersonalRepository, DistritoPersonalRepository>(new TransientLifetimeManager());
			container.RegisterType<IMunicipioRepository, MunicipioRepository>(new TransientLifetimeManager());
			container.RegisterType<IJuntaRepository, JuntaRepository>(new TransientLifetimeManager());
			container.RegisterType<IAclRepository, AclRepository>(new TransientLifetimeManager());
			container.RegisterType<IAgenteRepository, AgenteRepository>(new TransientLifetimeManager());
			container.RegisterType<IAreaRepository, AreaRepository>(new TransientLifetimeManager());
            container.RegisterType<IAfianzadoraRepository, AfianzadoraRepository>(new TransientLifetimeManager());
			container.RegisterType<IAcreditacionRepository, AcreditacionRepository>(new TransientLifetimeManager());
            container.RegisterType<IConcesionRepository, ConcesionRepository>(new TransientLifetimeManager());
            container.RegisterType<IComercializadoraRepository, ComercializadoraRepository>(new TransientLifetimeManager());
			container.RegisterType<ICiclosFechaRepository, CiclosFechaRepository>(new TransientLifetimeManager());
			container.RegisterType<IFoliosPermisoRepository, FoliosPermisoRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosNumeroRepository, CultivosNumeroRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosEstacionRepository, CultivosEstacionRepository>(new TransientLifetimeManager());
			container.RegisterType<ITenenciaRepository, TenenciaRepository>(new TransientLifetimeManager());
			container.RegisterType<IRiegoFuenteRepository, RiegoFuenteRepository>(new TransientLifetimeManager());
			container.RegisterType<IModuloRepository, ModuloRepository>(new TransientLifetimeManager());
			container.RegisterType<ISeccionRepository, SeccionRepository>(new TransientLifetimeManager());
			container.RegisterType<IRiegoSistemaRepository, RiegoSistemaRepository>(new TransientLifetimeManager());
			container.RegisterType<IAreaSeccionRepository, AreaSeccionRepository>(new TransientLifetimeManager());
			container.RegisterType<IEjidoRepository, EjidoRepository>(new TransientLifetimeManager());
			container.RegisterType<IUsuarioRepository, UsuarioRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosClasificacionRepository, CultivoClasificacionRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosEstacionRepository, CultivosEstacionRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivoRepository, CultivoRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosAguaRepository, CultivosAguaRepository>(new TransientLifetimeManager());
            container.RegisterType<ICultivosPropiedadRepository, CultivosPropiedadRepository>(new TransientLifetimeManager());
			container.RegisterType<ICultivosFechaRepository, CultivosFechaRepository>(new TransientLifetimeManager());
			container.RegisterType<IVariedadesGrupoRepository, VariedadesGrupoRepository>(new TransientLifetimeManager());
			container.RegisterType<IVariedadRepository, VariedadRepository>(new TransientLifetimeManager());
			container.RegisterType<IProduccionTipoRepository, ProduccionTipoRepository>(new TransientLifetimeManager());
			container.RegisterType<IFinanciamientoRepository, FinanciamientoRepository>(new TransientLifetimeManager());

            //Register Services mappings
			container.RegisterType<IEstadoService, EstadoService>(new TransientLifetimeManager());
			container.RegisterType<IDistritoService, DistritoService>(new TransientLifetimeManager());
			container.RegisterType<ICicloService, CicloService>(new TransientLifetimeManager());
			container.RegisterType<IDistritoPersonalService, DistritoPersonalService>(new TransientLifetimeManager());
			container.RegisterType<IMunicipioService, MunicipioService>(new TransientLifetimeManager());
			container.RegisterType<IJuntaService, JuntaService>(new TransientLifetimeManager());
			container.RegisterType<IAclService, AclService>(new TransientLifetimeManager());
			container.RegisterType<IAgenteService, AgenteService>(new TransientLifetimeManager());
			container.RegisterType<IAreaService, AreaService>(new TransientLifetimeManager());
            container.RegisterType<IAfianzadoraService, AfianzadoraService>(new TransientLifetimeManager());
            container.RegisterType<IAcreditacionService, AcreditacionService>(new TransientLifetimeManager());
            container.RegisterType<IConcesionService, ConcesionService>(new TransientLifetimeManager());
            container.RegisterType<IComercializadoraService, ComercializadoraService>(new TransientLifetimeManager());
			container.RegisterType<ICiclosFechaService, CiclosFechaService>(new TransientLifetimeManager());
			container.RegisterType<IFoliosPermisoService, FoliosPermisoService>(new TransientLifetimeManager());
			container.RegisterType<ICultivosNumeroService, CultivosNumeroService>(new TransientLifetimeManager());
			container.RegisterType<ICultivosEstacionService, CultivosEstacionService>(new TransientLifetimeManager());
			container.RegisterType<ITenenciaService, TenenciaService>(new TransientLifetimeManager());
			container.RegisterType<IRiegoFuenteService, RiegoFuenteService>(new TransientLifetimeManager());
			container.RegisterType<IModuloService, ModuloService>(new TransientLifetimeManager());
			container.RegisterType<ISeccionService, SeccionService>(new TransientLifetimeManager());
			container.RegisterType<IRiegoSistemaService, RiegoSistemaService>(new TransientLifetimeManager());
			container.RegisterType<IAreaSeccionService, AreaSeccionService>(new TransientLifetimeManager());
			container.RegisterType<IEjidoService, EjidoService>(new TransientLifetimeManager());
			container.RegisterType<IUsuarioService, UsuarioService>(new TransientLifetimeManager());
			container.RegisterType<ICultivoClasificacionService, CultivoClasificacionService>(new TransientLifetimeManager());
			container.RegisterType<ICultivosEstacionService, CultivosEstacionService>(new TransientLifetimeManager());
			container.RegisterType<ICultivoService, CultivoService>(new TransientLifetimeManager());
			container.RegisterType<ICultivosAguaService, CultivosAguaService>(new TransientLifetimeManager());
            container.RegisterType<ICultivosPropiedadService, CultivosPropiedadService>(new TransientLifetimeManager());
			container.RegisterType<ICultivosFechaService, CultivosFechaService>(new TransientLifetimeManager());
			container.RegisterType<IVariedadesGrupoService, VariedadesGrupoService>(new TransientLifetimeManager());
			container.RegisterType<IVariedadService, VariedadService>(new TransientLifetimeManager());
			container.RegisterType<IProduccionTipoService, ProduccionTipoService>(new TransientLifetimeManager());
			container.RegisterType<IFinanciamientoService, FinanciamientoService>(new TransientLifetimeManager());
            
            //Register crosscuting mappings
            container.RegisterType<ITraceManager, TraceManager>(new TransientLifetimeManager());
            container.RegisterType<INetworkHelper, NetworkHelper>(new TransientLifetimeManager());

        }

        /// <summary>
        /// Configure real container. Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        static void ConfigureRealContainer(IUnityContainer container)
        {
            container.RegisterType<IPermisosModuleContext, PermisosModuleLocalContext>(new PerExecutionContextLifetimeManager());
        }

        /// <summary>
        /// Configure fake container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        static void ConfigureFakeContainer(IUnityContainer container)
        {
            // Note: Generic register type method cannot be used here, 
            // PermisosModuleFakeContext cannot have implicit conversion to IPermisosModuleContext
            container.RegisterType(typeof(IPermisosModuleContext), typeof(PermisosModuleFakeContext), new PerExecutionContextLifetimeManager());
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns an injected object instance implementation for the requested interface
        /// IMPORTANT: It uses default IoC Container defined in AppSettings
        /// </summary>
        /// <exception cref="System.Configuration.ConfigurationErrorsException">
        /// defaultIocContainer AppSetting key not found
        /// </exception>
        public static T Resolve<T>()
        {
            //We use the default container specified in AppSettings
            string containerName = ConfigurationManager.AppSettings["defaultIoCContainer"];

            if (String.IsNullOrEmpty(containerName)
                ||
                String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException(Messages.exception_DefaultIOCSettings);
            }

            return Resolve<T>(containerName);


        }

        /// <summary>
        /// Returns an injected implementation for the requested interface
        /// It uses provided IoC Container passed as parameter
        /// </summary>
        public static T Resolve<T>(string containerName)
        {
            //check preconditions
            if (String.IsNullOrEmpty(containerName)
               ||
               String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException(Messages.exception_DefaultIOCSettings);
            }

            if (!_containersDictionary.ContainsKey(containerName))
                throw new InvalidOperationException(Messages.exception_ContainerNotFound);

            IUnityContainer container = _containersDictionary[containerName];

            return container.Resolve<T>();
        }

        #endregion

	}
}
